package bll

import (
	"context"
	"fmt"
	"proto/dto"
	log "signal/collector/logger"
	"signal/errors"
	"signal/lib/trace_log"
	"signal/model"
	"signal/utils"
)

var Request = &request{}

// 服务端向设备询问信息
type request struct {
}

func (r *request) init() func() {
	return func() {}
}

// BinaryReq 自定义向设备询问
func (r *request) BinaryReq(ctx context.Context, in *dto.BinaryRequest) (out *dto.BinaryRequestReply, err error) {
	var (
		requestId uint8
		resp      interface{}
	)
	out = &dto.BinaryRequestReply{}
	if requestId, err = Session.GetRequestID(in.Sn); err != nil {
		log.Error("get request id error ", err)
		return nil, err
	}

	k := RequestKey(in.Sn, model.CmdType(in.Hex[3]), requestId)
	in.Hex[2] = requestId
	trace_log.DebugLogger(in.Sn, "BinaryReq 请求:", fmt.Sprintf("%X", in.Hex), " key:", k)
	if resp, err = Session.TestRequest(in.Sn, k, in.Hex); err != nil {
		Session.ReleaseID(in.Sn, requestId)
		return nil, err
	}

	d := resp.([]byte)
	(*out).Response = d
	trace_log.DebugLogger(in.Sn, "BinaryReq 请求:", fmt.Sprintf("%X", in.Hex), " resp:", fmt.Sprintf("%X", d), " err:", err)
	return
}

// Battery 向设备询问电池信息
func (r *request) Battery(ctx context.Context, in *dto.BatteryRequest) (out *dto.BatteryReply, err error) {
	var (
		requestId uint8
		resp      interface{}
	)

	defer func() {
		trace_log.DebugLogger(in.Sn, "battery 查询 data:", utils.ToJsonString(in), " resp:", utils.ToJsonString(out), " err:", err)
	}()

	if requestId, err = Session.GetRequestID(in.Sn); err != nil {
		log.Error("get request id error ", err)
		return nil, err
	}
	k := RequestKey(in.Sn, model.CmdBattery, requestId)

	packet := model.BuildPacket(model.CmdBattery, requestId, nil)
	log.Debugf("send battery query to device: %X", packet.ToHexBytes())

	if resp, err = Session.SyncRequest(in.Sn, k, packet); err != nil {
		Session.ReleaseID(in.Sn, requestId)
		return nil, err
	}
	d := resp.([]byte)
	out, err = r.parseBatteryReply(d)
	trace_log.DebugLogger(in.Sn, "battery 查询 resp:", utils.ToJsonString(out))
	return
}

// Location 向设备询问GPS位置信息
func (r *request) Location(ctx context.Context, in *dto.LocationRequest) (out *dto.Location, err error) {
	var (
		requestId uint8
		resp      interface{}
	)

	defer func() {
		trace_log.DebugLogger(in.Sn, "Location 查询 data:", utils.ToJsonString(in), " resp:", utils.ToJsonString(out), " err:", err)
	}()

	if requestId, err = Session.GetRequestID(in.Sn); err != nil {
		log.Error("get request error ", err)
		return nil, err
	}
	k := RequestKey(in.Sn, model.CmdLocation, requestId)

	packet := model.BuildPacket(model.CmdLocation, requestId, nil)
	log.Debugf("send location query to device: %X", packet.ToHexBytes())

	if resp, err = Session.SyncRequest(in.Sn, k, packet); err != nil {
		Session.ReleaseID(in.Sn, requestId)
		return nil, err
	}
	d := resp.([]byte)
	out, err = r.parseLocation(d)
	return
}

// Controller 向设备询问控制器信息
func (r *request) Controller(ctx context.Context, in *dto.ControllerRequest) (out *dto.ControllerReply, err error) {
	var (
		requestId uint8
		resp      interface{}
	)

	defer func() {
		trace_log.DebugLogger(in.Sn, "Controller 查询 data:", utils.ToJsonString(in), " resp:", utils.ToJsonString(out), " err:", err)
	}()

	if requestId, err = Session.GetRequestID(in.Sn); err != nil {
		log.Error("get request error ", err)
		return nil, err
	}
	k := RequestKey(in.Sn, model.CmdController, requestId)

	packet := model.BuildPacket(model.CmdController, requestId, nil)
	log.Debugf("send location query to device: %X", packet.ToHexBytes())

	if resp, err = Session.SyncRequest(in.Sn, k, packet); err != nil {
		Session.ReleaseID(in.Sn, requestId)
		return nil, err
	}
	d := resp.([]byte)
	out, err = r.parseController(d)
	return
}

// FirmwareVersion 向设备询问固件版本信息
func (r *request) FirmwareVersion(ctx context.Context, in *dto.FirmwareVersionRequest) (out *dto.FirmwareVersionReply, err error) {
	var (
		requestId uint8
		resp      interface{}
	)

	defer func() {
		trace_log.DebugLogger(in.Sn, "FirmwareVersion 查询 data:", utils.ToJsonString(in), " resp:", utils.ToJsonString(out), " err:", err)
	}()

	if requestId, err = Session.GetRequestID(in.Sn); err != nil {
		log.Error("get request error ", err)
		return nil, err
	}
	k := RequestKey(in.Sn, model.CmdFirmware, requestId)

	packet := model.BuildPacket(model.CmdFirmware, requestId, nil)
	log.Debugf("send location query to device: %X", packet.ToHexBytes())

	if resp, err = Session.SyncRequest(in.Sn, k, packet); err != nil {
		Session.ReleaseID(in.Sn, requestId)
		return nil, err
	}
	d := resp.([]byte)
	out, err = r.parseFirmVersion(d)
	return
}

// 解析电池数据
func (r *request) parseBatteryReply(data []byte) (*dto.BatteryReply, error) {
	var (
		err         error
		status      dto.BatteryStatus
		loop        uint16
		remainTime  uint16
		voltage     uint16
		temperature uint16
		soh         uint16
		soe         uint16
		soc         uint16
		fault       uint8
		statusInt   uint8
	)

	utils.Reverse(data)
	log.Debugf("battery hex data: %X", data)

	reader := utils.NewBitReader(data)

	if loop, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if remainTime, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if voltage, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if temperature, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if soh, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if soe, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if soc, err = reader.ReadBits16(16); err != nil {
		return nil, err
	}
	if fault, err = reader.ReadBits8(8); err != nil {
		return nil, err
	}
	if statusInt, err = reader.ReadBits8(8); err != nil {
		return nil, err
	}

	switch statusInt {
	case 0:
		status = dto.BatteryStatus_Idle
	case 1:
		status = dto.BatteryStatus_Charge
	case 2:
		status = dto.BatteryStatus_Discharge
	case 3:
		status = dto.BatteryStatus_FeedbackCharge
	case 4:
		status = dto.BatteryStatus_FullCharge
	}

	// Idle false 0 100 100 667 438 65535 0
	out := &dto.BatteryReply{
		Status:      status,
		Soc:         int32(soc),
		Soe:         int32(soe),
		Soh:         int32(soh),
		Temperature: int32(temperature),
		Voltage:     int32(voltage),
		RemainTime:  int32(remainTime),
		Loop:        int32(loop),
	}
	if fault == 1 {
		out.Fault = true
	}
	log.Info("query Battery info is: ", *out)

	return out, nil
}

// 解析GPS位置数据
func (r *request) parseLocation(data []byte) (*dto.Location, error) {
	var (
		err      error
		lat      uint32
		lng      uint32
		gpsSpeed uint16
		bearing  uint16
	)

	utils.Reverse(data)
	log.Debugf("location hex data: %X", data)
	reader := utils.NewBitReader(data)

	if bearing, err = reader.ReadBits16(16); err != nil {
		log.Errorf("parse Lat error: %s", err)
		return nil, err
	}

	if gpsSpeed, err = reader.ReadBits16(16); err != nil {
		log.Errorf("parse Lat error: %s", err)
		return nil, err
	}

	if lng, err = reader.ReadBits32(32); err != nil {
		return nil, err
	}
	if lat, err = reader.ReadBits32(32); err != nil {
		return nil, err
	}

	location := &dto.Location{
		Lat:      int32(lat),
		Lng:      int32(lng),
		GpsSpeed: int32(gpsSpeed),
		Bearing:  int32(bearing),
	}

	log.Info("query Location is: ", *location)

	return location, nil
}

// 解析控制器数据
func (r *request) parseController(data []byte) (*dto.ControllerReply, error) {
	if data == nil || len(data) != 64 {
		return nil, errors.New("controller info data error")
	}

	// ignore ccu_uid 0-7
	mac := data[8:14]
	imei := data[14:29]
	imsi := data[29:44]
	iccid := data[44:64]

	info := &dto.ControllerReply{
		CcuMac:          fmt.Sprintf("%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]),
		SimImei:         string(imei),
		SimImsi:         string(imsi),
		SimIccid:        string(iccid),
		FirmwareVersion: "",
	}

	return info, nil
}

// 解析固件版本数据
func (r *request) parseFirmVersion(data []byte) (*dto.FirmwareVersionReply, error) {
	var (
		err  error
		info *dto.FirmwareVersionReply
	)

	info = &dto.FirmwareVersionReply{}
	utils.Reverse(data)
	reader := utils.NewBitReader(data)

	var a, b, c, d uint8
	if a, err = reader.ReadBits8(8); err != nil {
		return info, err
	}
	if b, err = reader.ReadBits8(8); err != nil {
		return info, err
	}
	if c, err = reader.ReadBits8(8); err != nil {
		return info, err
	}
	if d, err = reader.ReadBits8(8); err != nil {
		return info, err
	}

	info.FirmwareVersion = fmt.Sprintf("%d.%d.%d.%d", a, b, c, d)

	return info, nil
}
